/**
 * \file Type.cpp
 * \brief Implementation of Type subclasses
 * \author Wouter Caarls <w.caarls@tnw.tudelft.nl> 
 *
 * \verbatim
 * Copyright (c) 2005 Wouter Caarls, Delft University of Technology
 * 
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 * \endverbatim
 */

#include "Type.h"
#include "Value.h"
#include "LeftExpression.h"
#include "Symbol.h"
#include "Declaration.h"

UnknownType Unknown;
VoidType BasicVoid;
IntegerType BasicChar(NULL, 7);
IntegerType BasicUChar(NULL, 8);
IntegerType BasicShort(NULL, 15);
IntegerType BasicUShort(NULL, 16);
IntegerType BasicInteger(NULL, 31);
IntegerType BasicUInteger(NULL, 32);
FloatType BasicFloat(NULL, 32);
FloatType BasicDouble(NULL, 64);
PointerType NullPointer;
CodeType BasicCode;

/* ************************************************************************* */

std::string UnknownType::_toString(bool useTypeDefs, int indentation)
{
  return (char*)(CStr(indent(indentation)) << "unknown");
}

std::string UnknownType::toTerm()
{
  if (sym) return (char*)(CStr("TypeSpec([],TypeId(\"") << sym->identifier << "\"),[])");
  return "Unknown";
}

Value *UnknownType::makeValue()
{
  return new VoidValue();
}

Value *UnknownType::makeValue(double _data)
{
  plog.warning() << HDR << "trying to initialize unknown value" << std::endl;
  return new VoidValue();
}

/* ************************************************************************* */

std::string VoidType::_toString(bool useTypeDefs, int indentation)
{
  if (useTypeDefs && sym)
    return (char*)(CStr(indent(indentation)) << sym->identifier);
  else
    return (char*)(CStr(indent(indentation)) << "void");
}

std::string VoidType::toTerm()
{
  if (sym) return (char*)(CStr("TypeSpec([],TypeId(\"") << sym->identifier << "\"),[])");
  return "TypeSpec([], Void, [])";
}

Value *VoidType::makeValue()
{
  return new VoidValue();
}

Value *VoidType::makeValue(double _data)
{
   plog.warning() << HDR << "trying to initialize void value" << std::endl;
  return new VoidValue();
}

/* ************************************************************************* */

bool IntegerType::equals(Type *rhs)
{
  if (typeid(*rhs) != typeid(IntegerType))
    return false;
  
  IntegerType *irhs = dynamic_cast<IntegerType*>(rhs);
  
  return precision == irhs->precision;
}  

std::string IntegerType::_toString(bool useTypeDefs, int indentation)
{
  if (useTypeDefs && sym)
    return (char*)(CStr(indent(indentation)) << sym->identifier);
  else
    switch (precision)
    {
      case 7: return (char*)(CStr(indent(indentation)) << "char");
      case 8: return (char*)(CStr(indent(indentation)) << "unsigned char");
      case 15: return (char*)(CStr(indent(indentation)) << "short");
      case 16: return (char*)(CStr(indent(indentation)) << "unsigned short");
      case 31: return (char*)(CStr(indent(indentation)) << "int");
      case 32: return (char*)(CStr(indent(indentation)) << "unsigned int");
      case 63: return (char*)(CStr(indent(indentation)) << "long long");
      case 64: return (char*)(CStr(indent(indentation)) << "unsigned long long");
      default: 
	plog.err() << HDR << "Unknown integer precision " << precision << std::endl;
	exit(1);
    }
}

std::string IntegerType::toTerm()
{
  if (sym) return (char*)(CStr("TypeSpec([],TypeId(\"") << sym->identifier << "\"),[])");

  switch (precision)
  {
    case 7: return "TypeSpec([], Char, [])";
    case 8: return "TypeSpec([], Unsigned, [Char])";
    case 15: return "TypeSpec([], Short, [Int])";
    case 16: return "TypeSpec([], Unsigned, [Short, Int])";
    case 31: return "TypeSpec([], Int, [])";
    case 32: return "TypeSpec([], Unsigned, [Int])";
    case 63: return "TypeSpec([], Long, [Long, Int])";
    case 64: return "TypeSpec([], Unsigned, [Long, Long])";
    default: 
      plog.err() << HDR << "Unknown integer precision " << precision << std::endl;
      exit(1);
  }
}

Value *IntegerType::makeValue()
{
  return new IntegerValue();
}

Value *IntegerType::makeValue(double _data)
{
  return new IntegerValue((int) _data);
}

/* ************************************************************************* */

bool  FloatType::equals(Type *rhs)
{
  if (typeid(*rhs) != typeid(FloatType))
    return false;
  
  FloatType *frhs = dynamic_cast<FloatType*>(rhs);
  
  return precision == frhs->precision;
}  

std::string FloatType::_toString(bool useTypeDefs, int indentation)
{
  if (useTypeDefs && sym)
    return (char*)(CStr(indent(indentation)) << sym->identifier);
  else
    switch (precision)
    {
      case 32: return (char*)(CStr(indent(indentation)) << "float");
      case 64: return (char*)(CStr(indent(indentation)) << "double");
      case 128: return (char*)(CStr(indent(indentation)) << "long double");
      default: 
	plog.err() << HDR << "Unknown floating point precision " << precision << std::endl;
	exit(1);
    }
}

std::string FloatType::toTerm()
{
  if (sym) return (char*)(CStr("TypeSpec([],TypeId(\"") << sym->identifier << "\"),[])");

  switch (precision)
  {
    case 32: return "TypeSpec([], Float, [])";
    case 64: return "TypeSpec([], Double, [])";
    case 128: return "TypeSpec([], Long, [Double])";
    default: 
      plog.err() << HDR << "Unknown floating point precision " << precision << std::endl;
      exit(1);
  }
}

Value *FloatType::makeValue()
{
  return new FloatValue();
}

Value *FloatType::makeValue(double _data)
{
  return new FloatValue(_data);
}

/* ************************************************************************* */

PointerType::PointerType(Type *_to, int _elements) : to(_to), elements(_elements)
{
  deref = new PointerOf();
}

PointerType::PointerType(TypeSymbol *_sym, Type *_to, int _elements, PointerOf *_deref) :
  Type(_sym), to(_to), elements(_elements), deref(_deref)
{
  if (!deref)
    deref = new PointerOf();
}

PointerType::~PointerType()
{
  delete deref;
}

PointerType* PointerType::copy()
{
  return new PointerType(sym, to->copy(), elements, deref->copy());
}

bool PointerType::equals(Type *rhs)
{
  if (typeid(*rhs) != typeid(PointerType))
    return false;

  PointerType *prhs = dynamic_cast<PointerType*>(rhs);

/*
  if (elements != prhs->elements)
    return false;
*/

  return to->equals(prhs->to);  
}

std::string PointerType::_toString(bool useTypeDefs, int indentation)
{
  if (useTypeDefs && sym)
    return (char*)(CStr(indent(indentation)) << sym->identifier);
  else
  {
    switch (elements)
    {
      case -1:
	return (char*)(CStr(indent(indentation)) << to->toString() << "[]");
      case 0:
	return (char*)(CStr(indent(indentation)) << to->toString() << deref->toString());
      default:
	return (char*)(CStr(indent(indentation)) << to->toString() << "[" << elements << "]");
    }
  }
}

std::string PointerType::toPreString(bool useTypeDefs, int indentation)
{
  if (elements)
    return to->_toString(useTypeDefs, indentation);
  else
    return (char*)(CStr(to->_toString(useTypeDefs, indentation)) << deref->toString());
}

std::string PointerType::toPostString(int indentation)
{
  if (elements == -1)
    return "[]";
  else if (elements)
    return (char*)(CStr("[") << elements << "]");
  else
    return "";
}

std::string PointerType::toTerm()
{
  if (sym) return (char*)(CStr("TypeSpec([],TypeId(\"") << sym->identifier << "\"),[])");

  return (char*)(CStr("Address(") << to->toTerm() << ")");
}

Value *PointerType::makeValue()
{
  if (elements)
  {
    /* Create an array instead of a pointer */
    return new ArrayValue(this, to->makeValue(), elements);
  }
  else
  {
    /* Create an ordinary pointer, pointing to nothing */
    PointerValue *ptr =
      new PointerValue(this, 0, new Id(this, "__invalid_pointer__"), NULL);
    ptr->known = false;

    return ptr;
  }
}

Value *PointerType::makeValue(double _data)
{
  plog.warning() << HDR << "implicit cast to pointer not allowed" << std::endl;

  return makeValue();
}

int PointerType::size(void)
{
  if (elements)
    return elements * to->size();
  else
    return 4;
}

/* ************************************************************************* */

bool StructType::equals(Type *rhs)
{
  if (typeid(*rhs) != typeid(StructType))
    return false;

  StructType *srhs = dynamic_cast<StructType*>(rhs);

  if (subtypes.size() != srhs->subtypes.size())
    return false;

  if (!subtypes.empty())
    for (SI ii=subtypes.begin(), jj=srhs->subtypes.begin(); ii != subtypes.end();
	 ++ii, ++jj)
      if (!(*ii)->type->equals((*jj)->type))
	return false;

  return true;
}

void StructType::push_back(SubType *subtype)
{
  subtypes.push_back(subtype);
}

std::string StructType::_toString(bool useTypeDefs, int indentation)
{
  if (useTypeDefs && sym)
    return (char*)(CStr(indent(indentation)) << sym->identifier);
  else
  {
    CStr res;
    
    res << indent(indentation) << "struct {\n";
    
    if (!subtypes.empty())
      for (SI ii=subtypes.begin(); ii != subtypes.end(); ++ii)
	res << (*ii)->type->toPreString(useTypeDefs, indentation+1) << " " << (*ii)->name
	    << (*ii)->type->toPostString() << ";\n";
    res << indent(indentation) << "}";
    
    return (char*)res;
  }
}

std::string StructType::toTerm()
{
  if (sym)
  {
    CStr res;

    res << "TypeSpec([],TypeId(\"";
    res << sym->identifier;
    res << "\"),[])";
 
    return (char*)res;
  }

  return "NotImplemented";
}

Value *StructType::makeValue()
{
  StructValue *val = new StructValue(this);

  if (!subtypes.empty())
    for (SI ii=subtypes.begin(); ii != subtypes.end(); ++ii)
      val->push_back(new Value*((*ii)->type->makeValue()));

  return val;
}	

Value *StructType::makeValue(double _data)
{
  plog.warning() << HDR << "implicit cast to struct not allowed" << std::endl;

  return new VoidValue();
}

int StructType::findMemberIndex(std::string name)
{
  unsigned int ii;

  for (ii=0; ii != subtypes.size(); ++ii)
    if (subtypes[ii]->name == name)
      return ii;

  return -1;
}

int StructType::size()
{
  int _size = 0;

  if (!subtypes.empty())
    for (SI ii=subtypes.begin(); ii != subtypes.end(); ++ii)
      _size += (*ii)->type->size();

  return _size;
}

/* ************************************************************************* */

std::string CodeType::_toString(bool useTypeDefs, int indentation)
{
  return (char*)(CStr(indent(indentation)) << "code");
}

std::string CodeType::toTerm()
{
  if (sym) return (char*)(CStr("TypeSpec([],TypeId(\"") << sym->identifier << "\"),[])");

  return "TypeSpec([], Code, [])";
}

Value *CodeType::makeValue()
{
  return new CodeValue();
}

Value *CodeType::makeValue(double _data)
{
  plog.warning() << HDR << "implicit cast to code not allowed" << std::endl;
  return new VoidValue();
}

/* ************************************************************************* */

Type *constructType(Type *base, std::vector<PointerOf*> derefs, SymbolTable *symbols)
{
  Type *type = base;
  TypeSymbol *tdef=NULL;

  if (!derefs.empty())
    for (IdDecl::DI ii=derefs.begin(); ii != derefs.end(); ++ii)
    {
      if (symbols)
        tdef = dynamic_cast<TypeSymbol*>
  	    (symbols->findSymbol((char*)(CStr(type->toString()) << (*ii)->toString())));

      if (!tdef)
      {
        PointerType *ptype = new PointerType(type);
        ptype->deref = (*ii)->copy();
 
        type = ptype;
 
        if (symbols)
        {
  	  tdef = new TypeSymbol((char*)(CStr(type->toString())), type);
 	  symbols->addSymbol(tdef);
        }
      }
      else   
        type = tdef->data;
    }

 return type;
}

Type *constructType(Type *base, std::vector<IntegerValue*>&bounds, SymbolTable *symbols)
{
  Type *type = base;
  TypeSymbol *tdef=NULL;

  for (int ii=bounds.size()-1; ii >= 0; --ii)
  {
    if (symbols)
    {
      CStr tname;

      tname << type->toString() << "[";
      if (bounds[ii]->castToInteger() != -1) tname << bounds[ii]->castToInteger();
      tname << "]";
      
      tdef = dynamic_cast<TypeSymbol*>(symbols->findSymbol((char*)tname));
    }

    if (!tdef)
    {
      type = new PointerType(type, bounds[ii]->castToInteger());

      if (symbols)
      {
	tdef = new TypeSymbol((char*)(CStr(type->toString())), type);
	symbols->addSymbol(tdef);
      }
    }
    else
      type = tdef->data;
  }
  
  return type;
}
